package core

import (
	"context"
	"encoding/binary"
	"fmt"
	"io"
	"net"
	"os"
	"strings"
	"time"

	uuid "github.com/satori/go.uuid"
)

// TCPServer 用于与Dispatcher建立TCP通道、控制推流
type TCPServer struct {
	Port int
}

type Pusher struct {
	conn net.Conn
}

func (p *Pusher) Receive(buf []byte) int {
	n, err := p.conn.Read(buf)
	if err != nil {
		G.Log.Error(err)
		return 0
	}
	return n
}

func (p *Pusher) RemoteAddr() net.Addr {
	return p.conn.RemoteAddr()
}

func (svr *TCPServer) Start(serverName string, ctx context.Context, disp *Dispatcher, handleEnterFunc func(context.Context, net.Conn, *Dispatcher)) {
	listener, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0:%d", svr.Port))
	if err != nil {
		G.Log.Error(err)
		panic(err)
	}

	G.Log.Infof("Tcp %s receiver has started at :%d", serverName, svr.Port)

	for {
		select {
		case <-ctx.Done():
			G.Log.Errorf("TCP %s server has been canceled.", serverName)
			return
		default:
		}
		conn, err := listener.Accept()
		if err != nil {
			G.Log.Error("listener.Accept Error: ", err)
			continue
		}

		go handleEnterFunc(ctx, conn, disp)
	}
}

// OnStreamPusherEnter 视频推流客户端进入时的操作
func OnStreamPusherEnter(ctx context.Context, conn net.Conn, disp *Dispatcher) {
	G.Log.Infof("Puser stream [%s] enter", conn.RemoteAddr().String())
	defer conn.Close()
	var buf [512000]byte
	var mCh *Channel
	for {
		select {
		case <-ctx.Done():
			G.Log.Error("TCPServer has been canceled.")
			return
		default:
		}
		//读取包头
		if _, err := io.ReadFull(conn, buf[:2]); err != nil {
			G.Log.Infof("Pusher[%s] leaved", conn.RemoteAddr().String())
			_ = conn.Close()
			return
		}
		bodyLen := binary.LittleEndian.Uint16(buf[:2])
		// 读包体
		if _, err := io.ReadFull(conn, buf[:bodyLen]); err != nil {
			G.Log.Infof("Pusher[%s] leaved", conn.RemoteAddr().String())
			_ = conn.Close()
			return
		}
		if bodyLen < 10 {

			// 包长度小于10，是告知通道号的,也就是客户端第一次初始化的时候通知服务器需要发送流数据
			chCode := BytesToString(buf[:bodyLen])
			ch, ok := disp.ChannelMap[chCode]
			if ok {
				ch.pusher = conn
				mCh = ch // 通过 pusher发来的通道号 匹配到 channel实例
				G.Log.Infof("Puser[%s][%s] matched", conn.RemoteAddr().String(), chCode)
			} else {
				G.Log.Errorf("Puser[%s][%s] can not matched", conn.RemoteAddr().String(), chCode)
			}
		} else {
			// 包长度大于10，才是实际的流媒体数据
			mCh.OnPushingData(buf[:bodyLen])
		}
	}
}

// OnCapturePusherEnter 截图客户端进入时的操作
func OnCapturePusherEnter(ctx context.Context, conn net.Conn, disp *Dispatcher) {
	G.Log.Infof("Pusher capture [%s] has enter", conn.RemoteAddr().String())

	uuidStr := uuid.NewV4().String()
	tempFilePath := G.TempPath + "/" + uuidStr + ".jpg"

	// 创建一个临时图片文件
	file, err := os.Create(tempFilePath)
	if err != nil {
		G.Log.Error("Create image fail, fail reason", err)
		return
	}
	chNo := "" // 定义一个空的通道号
	readChan := make(chan int, 1)

	// 定义一个缓冲区用来接收文件流
	go func() {
		buf := make([]byte, 1024)
		total := 0
		defer func() {
			conn.Close()
			file.Close()
		}()
		for {
			// 往文件中循环写流
			n, err := conn.Read(buf)
			if err == io.EOF {
				readChan <- total
				break
			}
			if chNo == "" { // 说明接收的是通道号
				chNo = string(buf[:5])
				chNo = strings.Trim(chNo, " ")
				continue
			}
			G.Log.Infof("Receive capture buf length is %d", n)
			if _, err := file.Write(buf[:n]); err != nil {
				G.Log.Error("Copy image fail, fail reason:", err)
				break
			}
			total += n
		}

	}()

	select {
	case t := <-readChan:
		G.Log.Infof("Receive capture buf total length is %d, channel is %s", t, chNo)
		// 将文件全部写完后的的临时文件路径放入通道中
		ch := captureChans[chNo]
		ch <- tempFilePath
	case <-time.After(time.Second * 25):
		G.Log.Infof("Receive img buf timeout")
		os.Remove(tempFilePath)
		return
	}
}
